/**
	Date: 22.02.07
	Time Module holds Timer templates. This is made to make it possible for the user
	to use any time format necessery. As the most common way is to use floats in range 
	[0,1] there is a chance programmers would like to hold it in frames, miliseconds or 
	other format. 
*/
module necrophilia.Time;

/**
	Enumeration used for holding state of the Timer.
*/
enum TimerState{
	Forward=0, /// Timer ticking foreward. 
	Pause, /// Timer paused
	Backward, /// Timer going down
	ForwardLoop, /// Timer ticking foreward and in Loop Mode
	BackwardLoop, ///Timer ticking foreward and in Loop Mode
	Stop /// Timer riched the border and stoped
}

/*
	Template for timer class used in Necrophilia. Every Timer class must inharit from that one.
	Examples:
	------------
	alias Timer!(float) FloatTimer;
	FloatTimer foo=new FloatTimer(0.01,0);
	------------
	Parameters:
	Time = Time type. it must have +, - , <= , >=, += , -= operations defined
	Notes:
	Foreward tick is default
*/  
class Timer (Time) {
	alias Time TimeType; /// held to get the type the templete is fit by
	
	protected Time _value; // current value of timer
	protected Time _speed; // timers speed
	protected TimerState _state; // timers state
	
	
	/**
		Timer constructor
		Params:
		speed = timer speed
		startValue = initial value
	*/
	this(Time speed, Time startValue){
		_value=startValue;
		_speed=speed;
		_state=TimerState.Forward;
	}
	
	/**
		Method updating the timer
		Returns:
		False if the state of the timer is unrecognized 
	*/
	bool update(){
		if(_state==TimerState.Pause) return true;
		if(_state==TimerState.Forward) {
			_value+=_speed;
			return true;
		}
		if(_state==TimerState.Backward){
			_value-=_speed;
			return true;
		}
		return false;
	}
	
	/**
		Makes timer to pause
	*/
	void pause() { 
		_state=TimerState.Pause; 
	}
	
	/**
		Sets timer to tick foreward
	*/
	void playForward() { 
		_state= TimerState.Forward;
	} 
	
	/**
		Sets timer to tick backward
	*/
	void playBackward() {
		 _state=TimerState.Backward;
	}
	
	/** 
		Diagnostic function
		Returns:
		True if timer is paused
	*/
	bool isPaused() { 
		return (_state==TimerState.Pause); 
	}
	
	/** 
		Diagnostic function
		Returns:
		True if timer is ticking forward
	*/
	bool isPlayingForward() {
		 return (_state==TimerState.Forward); 
	}
	
	/** 
		Diagnostic function
		Returns:
		True if timer is ticking backword
	*/
	bool isPlayingBackward() {
		return (_state==TimerState.Backward) ; 
	}
	
	/** 
		Property
		Returns:
		current timer value
	*/
	Time time(){
		return _value;
	}
	
	/** 
		Setting timers time value to new one
		Params:
		time=new time for the timer
		Returns:
		timers value before change
	*/
	Time setTime(Time time){
		Time prev=_value;
		_value=time;
		return prev;
	}
	
	/** 
		Property
		Returns:
		current timers speed
	*/
	Time speed(){
		return _speed;
	}
	
	/** 
		Setting timers speed value to new one
		Params:
		speed=new speed for the timer
		Returns:
		timers speed before speed
	*/
	Time setSpeed(Time speed){
		Time prev=_speed;
		_speed=speed;
		return prev;
	}
}

/** 
	Extension of Timer template class. With option of loops. Lets say we have timer ticking from 0 to 10.
	It is matematicly [0,10). Loop length is 10 in that case.  Lets say speed is 1.  When the value is 10 it will become 0.
	Examples:
	-------------
	alias LoopTimer!(float) FloatTimer;
	FloatTimer foo=new FloatTimer(0.01,0,0,1);
	------------
	Parameters:
	Time = Time type. it must have +, - , <= , >=, += , -= operations defined
	Notes:
	Remember that states here have a bit different meaning.
	Pause is still a pause, but
	* Forward and Backwords as it is without the loop will make timer to change state to Stop, when reaching border
	* ForwardLoop (default) and BackwordLoop are loops as written above, will go in their direction 
*/  
class LoopTimer(Time): Timer!(Time) {
	protected Time _min, _max, _length; // borders data
	protected bool _isSet;
	
	/**
		LoopTimer constructor
		Params:
		speed = timer speed
		startValue = initial value
		min = left border
		max = right border
		so the loop is in [min,max)
	*/
	this(Time speed, Time startValue, Time min, Time max){
		super(speed,startValue);
		setNewBorders(min, max);
		_state=TimerState.ForwardLoop;
	}
	
	/**
		LoopTimer constructor
		Params:
		speed = timer speed
		startValue = initial value
		Note:
		Loop Border here is not set, so both, min and max are this same - set on default value, for example 
		NaN for floats. So it's necessery to call setNewBorders before first update
	*/
	this(Time speed, Time startValue){
		super(speed,startValue);
		_state=TimerState.ForwardLoop;
		_isSet=false;
	}
	
	/** 
		Function for setting the border
		Params:
		min = left border
		max = right border
		so the loop is in [min,max)
	*/
	void setNewBorders(Time min, Time max){
		_min=min;
		_max=max;
		_length=_max-_min;
		_isSet=true;
		assert(_speed<_length,"speed must be smaller then space between the borders [min,max) ");
	}
	
	/**
		Method updating the timer
		Returns:
		False if the state of the timer is unrecognized 
		Throws:
		Exception when max and min of the border are the same
		Probably because the borders were not set
	*/
	override bool update(){
		if(!_isSet) throw new Exception ("min and max of the timer border are the same. Probably the border was not set or set not properly");		
		if( _state==TimerState.Stop) return true;
		
		if (super.update()){
			if(_value>=_max) {
				_value=_max;
				_state=TimerState.Stop;
				return true;
			}
			
			if(_value<_min){
				_value=_min;
				_state=TimerState.Stop;
				return true;
			}
			
			return true;
			
		} else {
			
			if(_state==TimerState.ForwardLoop) 	{
				_value+=_speed;
				while(_value>=_max) _value-=_length;
				return true;
			}
			
			if(_state==TimerState.BackwardLoop)  {
				_value-=_speed;
			   while(_value<_min)  _value+=_length;
				return true;
			}
			
		}
		
		return false;
	}
	
	/** 
		Diagnostic function
		Returns:
		True if timer is stopped
	*/
	bool isStoped() { 
		return (_state==TimerState.Stop); 
	}
	
	/** 
		Diagnostic function
		Returns:
		True if timer is playing a loop and forward
	*/
	bool isPlayingForwardLoop() {
		 return (_state==TimerState.ForwardLoop); 
	}
	
	/** 
		Diagnostic function
		Returns:
		True if timer is playing a loop and backward
	*/
	bool isPlayingBackwardLoop() {
		return (_state==TimerState.BackwardLoop) ; 
	}
	
	/**
		Sets timer to tick foreward in a loop
	*/
	void playForwardLoop() { 
		_state= TimerState.ForwardLoop;
	} 
	
	/**
		Sets timer to tick backward in a loop
	*/
	void playBackwardLoop() {
		 _state=TimerState.BackwardLoop;
	}
	
	/**
		Diagnostic method
		Returns:
		current low border
	*/
	Time min(){
		return _min;
	}

	/**
		Diagnostic method
		Returns:
		current upper border
	*/	
	Time max(){
		return _max;
	}
}

unittest {
	/* Timer test */
	{
		Timer!(float) timer=new Timer!(float)(0.1,0);
		assert(timer.time==0);
		assert(timer.isPlayingForward);
		timer.update;
		assert(timer.time==0.1f);
		timer.pause;
		timer.update;
		assert(timer.time==0.1f);
		assert(timer.isPaused);
		assert(!timer.isPlayingForward);
		assert(!timer.isPlayingBackward);
		timer.playBackward;
		timer.update;
		timer.update;
		assert(timer.time==-0.1f);
		assert(timer.speed==0.1f);
		assert(timer.setSpeed(0.9f)==0.1f);
		timer.update;
		assert(timer.time==-1.0f); 
	}
	
	/* LoopTimer tests */
	{
		LoopTimer!(float) timer=new LoopTimer!(float)(0.3,0,0,0.5);
		timer.playForward;
		timer.update; 
		timer.update;
		assert(timer.time==0.5f);
		assert(timer.isStoped);
	}
	
	{
		LoopTimer!(float) timer=new LoopTimer!(float)(0.3,0,0,0.5);
		timer.update; 
		timer.update;
		assert(timer.time>=0.1f && timer.time<=0.10001f);
		assert(!timer.isStoped);
		assert(timer.isPlayingForwardLoop);
	}
	
	
}